package de.hsowl.tod4a.global.crypto;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Provides encryption capabilities. Supported methods: AES 128, AES 192 and AES 256.
 * @author Sebastian Witt
 *
 */
public class AES {
	
	/**
	 * Encryption
	 * @param seed - your key
	 * @param cleartext - your text
	 * @param runden 
	 * @param method 
	 * @return
	 */
	public static byte[] encrypt(byte[] seed, byte[] cleartext, int rounds, int method) {
		try {

			byte[] rawKey = getRawKey(seed, method);
			byte[] encrypted = cleartext;
			for (int i = 0; i < rounds; i++) {
				encrypted = encrypt(rawKey, encrypted);
			}

			return encrypted;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Decryption
	 * @param seed
	 * @param encrypted
	 * @param runden
	 * @param method
	 * @return
	 */
	public static byte[] decrypt(byte[] seed, byte[] encrypted, int rounds, int method) {
		try {

			byte[] rawKey = getRawKey(seed, method);
			byte[] decrypted = encrypted;

			for (int i = 0; i < rounds; i++) {
				decrypted = decrypt(rawKey, decrypted);

			}

			return decrypted;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Generates a key
	 * @param seed
	 * @param method
	 * @return
	 * @throws Exception
	 */
	private static byte[] getRawKey(byte[] seed, int method) throws Exception {
		String[] params = getMethod(method);
		String name = params[0];
		int key_size = Integer.parseInt(params[1]);

		KeyGenerator kgen = KeyGenerator.getInstance(name);
		SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
		sr.setSeed(seed);
		kgen.init(key_size, sr); 
		SecretKey skey = kgen.generateKey();
		byte[] raw = skey.getEncoded();
		return raw;
	}

	/**
	 * Returns the encryption method
	 * 
	 * @param method
	 * @return String[0]=algorithmus; String[1]=key size
	 */
	private static String[] getMethod(int method) {
		int key_size = 0;
		String name = null;
		switch (method) {
		case CryptoUnit.AES_128:
			name = "AES";
			key_size = 128;
			break;
		case CryptoUnit.AES_192:
			name = "AES";
			key_size = 192;
			break;
		case CryptoUnit.AES_256:
			name = "AES";
			key_size = 256;
			break;
		default:
			throw new RuntimeException("error:getMethod():Unknown encryption method");
		}
		return new String[] { name, String.valueOf(key_size) };
	}

	/**
	 * Encryption
	 * @param raw
	 * @param clear
	 * @return
	 * @throws Exception
	 */
	private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
		byte[] encrypted = cipher.doFinal(clear);
		return encrypted;
	}

	/**
	 * Decryption
	 * @param raw
	 * @param encrypted
	 * @return
	 * @throws Exception
	 */
	private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		byte[] decrypted = cipher.doFinal(encrypted);
		return decrypted;
	}
}
